# @Author:黄景涛
# @Date: 2024/3/15
"""
解析断言表达式的模块，支持以下场景：
    1、基本：
        - equals: { message: true }                 bool值
        - equals: { message: 200 }                  数字
        - equals: { message: success }              字符串
    2、高级：
        - equals: { message: $request.name }        当前请求参数
        - equals: { message: $response.name }       当前响应参数
        - equals: { message: $request.name[0] }     当前请求/响应参数，并切片,支持所有切片写法，如：[0]、[-1]、[1:3]、[1:3:2]
        - equals: { message: $request.result['name'] }  当前请求/响应参数，支持字典通过key获取value
        - equals: { message: $request.result[0]['name'] }  当前请求/响应参数，支持切片+字典获取value
        - equals: { message: $func([1,2,3], 2) }    自定义函数式
        - equals: { message: $len('abc') }          内置函数式
        - equals: { message: $len(abc) }            函数参数，两种写法：可加引号、可不加引号
        - equals: { message: $func($request.name[0], 2) }    函数参数，支持传入请求/响应参数（有切片、无切片）
        --PS：多参数的函数中，$request.不能加引号
"""
import sys

import jsonpath
import re

import yaml

from commons.hjt_tools import DebugTalk


current_module = sys.modules[__name__]


# print(current_module.__doc__)


class AssertExpr:
    tools_module = DebugTalk()  # 工具模块

    def __init__(self, assert_expr: str, case_data: dict, response_data):
        """
        :param assert_expr: 需要解析的断言表达式，如：$request.name、$len($request.name[2])、$func1($request.fields[0], 1)
        :param case_data: 接口请求数据
        :param response_data: 响应数据
        """
        self.assert_expr = assert_expr
        self.case_data = case_data
        self.response_data = response_data

    def args_from_str_to_tuple(self, str_args: str):
        """
        将字符串形式，转化为元组形式，并保留参数的原始类型
        :param str_args: 如 “[1, 2, 3], 1, {'k': 1, 'b': 2}” 、 “[1, 2, 3], 1, {'k': 1, 'b': 2}, $request.name”
        :return:
        """
        pattern_req = r"\$request\.\w+(\[[\d:-]*\])?"
        pattern_resp = r"\$response\.\w+(\[[\d:-]*\])?"

        try:
            # 请求参数回填
            req_arg = self.__get_request_arg(re.search(pattern_req, str_args).group())
            final_arg = re.sub(pattern_req, req_arg, str_args)
        except AttributeError:
            try:
                # 响应参数回填
                resp_arg = self.__get_response_arg(re.search(pattern_resp, str_args).group())
                final_arg = re.sub(pattern_resp, resp_arg, str_args)
            except AttributeError:
                # 无需回填，直接返回原参数
                final_arg = str_args

        try:
            func_args = eval(final_arg)
            # 处理非元组，如：'abc'、[1,2,3]、{'a':1, 'b':2}
            if not isinstance(func_args, tuple):
                func_args = (func_args,)
        except (SyntaxError, NameError):
            # 兼容处理：参数未加引号
            func_args = (final_arg,)
        # 处理('',)
        if func_args[0] == '':
            func_args = tuple()

        return func_args

    def __get_function(self, s: str):
        """
        :param s: 函数表达式，如: $f(func,$request.name)、$f(func, [1,2,3], 1)
        :return: 函数执行结果
        """
        # print(f'进入函数处理>>: {s}')

        pattern = r'\$(\w+)\((.*)?\)'
        func_expr = re.match(pattern, s).group()
        # 1. 拆解函数表达式
        func_name, args = re.match(pattern, s).groups()
        # print(f'函数名：{func_name}')
        # 2.入参处理
        # print(f'处理前的参数:{args}')
        args_tuple = self.args_from_str_to_tuple(args)
        # print(f'处理后的函数:{func_name}{args_tuple}')

        # 3.获取函数对象，并调用
        try:
            # 执行自定义函数
            func_obj = getattr(self.tools_module, func_name)
        except AttributeError:
            try:
                # 兜底获取内置函数
                func_obj = getattr(__builtins__, func_name)
                # func_obj = __builtins__.get(func_name)
            except AttributeError:
                error_msg = f'【hjt_tools】文件中，未定义函数【{func_name}】'
                raise Exception(error_msg)
        try:
            return func_obj(*args_tuple)
        except Exception as e:
            args_error_msg = f'函数【{func_name}】执行报错，<{e.__class__.__name__}>:{e} ——— 热加载的数据: {func_expr}'
            raise Exception(args_error_msg)

    def __get_request_arg(self, arg_str):
        """
        断言语句中的请求参数处理
        :param arg_str: 需要提取的$request.xx、$request.xx[idx]、$request.xx[1:3]
        :return: 参数值
        """
        # print(f'进入请求参数处理>>: {arg_str}')
        _param = None

        # 1.获取请求中的json或params参数，作为jsonpath的数据池
        try:
            json_ = self.case_data.get('json')
            params_ = self.case_data.get('params')
        except AttributeError:
            error_msg = f'当前请求不存在request、json或params参数'
            raise Exception(error_msg)

        if json_:
            _param = json_
        elif params_:
            _param = params_

        # 2.对$request.name、$request.name[1:3]，进行jsonpath提取并处理切片
        return self.__arg_from_jsonpath_and_slice(arg_str, _param)

    def __get_response_arg(self, arg_str):
        """
        断言语句中的响应参数处理
        :param key: 需要提取的$response.xx ， 或者其他
        :return: 参数值
        """
        # print(f'进入响应参数处理>>: {arg_str}')
        # 对$response.name、$response.name[1:3]，进行jsonpath提取并处理切片
        return self.__arg_from_jsonpath_and_slice(arg_str, self.response_data)

    @classmethod
    def __arg_from_jsonpath_and_slice(cls, arg_str, data_pool):
        """
        $request.name、$request.name[0]、$request.name[1:3]、$request.name[1:3:2]
        :param arg_str: name、name[1:3:2]
        :param data_pool: jsonpath提取的数据池
        :return:
        """
        res_ = None
        # TODO 拆解 $request和 .name[1:3]
        # TODO 拆解 $request 和 .result['age']
        key = arg_str.split('.')[-1]

        pattern = r"(?P<key>\w+)(?P<slice>.*)?"
        # $request.name中的name
        key_ = re.search(pattern, key).group('key')
        # 切片[0]、[1:3]、[1:3:2]
        slice_ = re.search(pattern, key).group('slice')
        # jsonpath方式，从数据池中，提取key
        expr = f'$..{key_}'
        val = jsonpath.jsonpath(data_pool, expr=expr)

        #     # 提取不到，则返回False
        #     if val == '':
        #         return ''
        #     elif not val:
        #         return False
        #
        #     if not slice_:
        #         # jsonpath提取结果列表中只有一个，则返回这个值；否则返回所有的值
        #         if isinstance(val, list):
        #             if len(val) == 1:
        #                 return str(val[0])
        #         return str(val)
        #     else:
        #         # 执行切片
        #         return str(eval(str(val) + slice_))

        # 提取不到，则返回False
        if val == '':
            res_ = ''
        elif not val:
            res_ = False

        # 提取结果只有一个，返回jsonpath列表的这个元素
        if isinstance(val, list):
            if len(val) == 1:
                res_ = val[0]

        # 有附加内容，则执行eval函数
        if not slice_:
            return str(res_)
        else:
            return str(eval(str(res_) + slice_))

    def main(self):
        """
        解析断言表达式
        :return: 解析后的参数值
        """
        try:
            s = self.assert_expr.strip()
            # 不是$开头的字符串，直接返回
            if not s.startswith('$'):
                return self.assert_expr
        except AttributeError:
            # 非字符串，直接返回
            return self.assert_expr

        # 解析$request.xx
        if s.startswith('$request.'):
            return self.__get_request_arg(s)
        # 解析$response.xx
        elif s.startswith('$response.'):
            return self.__get_response_arg(s)
        # 解析函数式$f(func_name, *args)
        elif s.startswith('$'):
            return self.__get_function(s)
        else:
            error_msg = f'【{s}】书写格式错误'
            raise Exception(error_msg)


if __name__ == '__main__':
    pass
